package cn.nawang.ebeim.server.service;

import cn.nawang.ebeim.server.constants.Config;
import cn.nawang.ebeim.server.constants.Constant;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import org.apache.commons.io.IOUtils;
import org.jclouds.ContextBuilder;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.features.ContainerApi;
import org.jclouds.openstack.swift.v1.features.ObjectApi;
import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
import org.jclouds.openstack.swift.v1.options.PutOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created by GanJc on 2015-11-10 17:34
 */
public class SwiftStorageService implements StorageService {

    private static final Logger LOG = LoggerFactory.getLogger(SwiftStorageService.class);

    private static final long FIVE_GB = 5368709120l;

    private SwiftApi swiftApi;

    public SwiftStorageService() {
        Iterable<Module> modules = ImmutableSet.<Module>of();
        String provider = Config.SWIFT_PROVIDER;
        String identity = Config.SWIFT_IDENTITY; //tenantName:userName
        String credential = Config.SWIFT_CREDENTIAL;
        String endpoint = Config.SWIFT_ENDPOINT;
        swiftApi = ContextBuilder.newBuilder(provider).endpoint(endpoint)
                .credentials(identity, credential).modules(modules).buildApi(SwiftApi.class);
    }

    public void uploadFile(String dataSourceId, String signature,String fileAbsPath) {
        this.uploadObject(dataSourceId, signature, fileAbsPath);
    }

    public void downloadFile(String dataSourceId, String signature,String downloadPath) {
        this.downObject(dataSourceId, signature, downloadPath);
    }

    /**
     * 创建container容器
     *
     * @param containerName container容器名
     */
    private void createContainer(String containerName) {
        LOG.info("Create Container {} start.", containerName);
        ContainerApi containerApi = swiftApi.getContainerApi("RegionOne");
        CreateContainerOptions options = CreateContainerOptions.Builder
                .metadata(ImmutableMap.of("key1", "value1", "key2", "value2"));
        containerApi.create(containerName, options);
        LOG.info("Create Container {} success.", containerName);
    }

    /**
     * 往容器中添加对象
     *
     * @param containerName 容器名称
     * @param objectName    对象名称
     * @param uploadPath    上传文件的路径
     */
    private void uploadObject(String containerName, String objectName, String uploadPath) {
        LOG.info("Upload Object {}", uploadPath);
        long begin = System.currentTimeMillis();
        File file = new File(uploadPath);
        if (!file.exists()) {
            LOG.error("Upload Object failed {}:{} ,file not exists !",containerName, objectName);
            return;
        }
        long length = file.length();
        if (length >= FIVE_GB) {
            LOG.error("Upload Object failed {}:{} ,file too big: {} bytes !", containerName,objectName,length);
            return;
        }
        ObjectApi objectApi = swiftApi.getObjectApi("RegionOne", containerName);
        if (objectApi == null) {
            createContainer(containerName);
            objectApi = swiftApi.getObjectApi("RegionOne", containerName);
        }
        Payload payload = Payloads.newPayload(file);
        try {
            objectApi.put(objectName, payload,
                    PutOptions.Builder.metadata(ImmutableMap.of("key1", "value1")));
        } catch (Exception e) {
            LOG.error("objectApi.put exception,try create container:" + containerName,e);
            createContainer(containerName);
            objectApi.put(objectName, payload,
                    PutOptions.Builder.metadata(ImmutableMap.of("key1", "value1")));
        } finally {
            IOUtils.closeQuietly(payload);
        }
        long end = System.currentTimeMillis() - begin;
        LOG.info("Upload Object {} to {} success. fileSize: {} byte. cost: {} ms .", objectName, containerName, length, end);
    }

    /**
     * 从容器中下载对象
     *
     * @param containerName 容器名称
     * @param objectName    对象名称
     * @param downloadPath  下载文件夹的路径
     */
    private void downObject(String containerName, String objectName, String downloadPath) {
        LOG.info("Download Object {} start.", downloadPath);
        long begin = System.currentTimeMillis();
        String tempFileName =   Config.WORKING_DIR + Constant.FILE_DOWNLOAD + File.separator +containerName
                + File.separator + Constant.TEMP_FILE_PREFIX + objectName;
        File tempFile = new File(tempFileName);
        File finalFile = new File(downloadPath);
        try {
            ObjectApi objectApi = swiftApi.getObjectApi("RegionOne", containerName);
            if (objectApi == null) {
                LOG.error("Download Object failed! {}:{} ,file not found !",containerName, objectName);
                return;
            }
            SwiftObject swiftObject = objectApi.get(objectName);
            if (swiftObject == null) {
                LOG.error("Download Object failed! {}:{} ,file not found !",containerName, objectName);
                return;
            }
            if (!tempFile.getParentFile().exists()) {
                tempFile.getParentFile().mkdirs();
            }
            Payload payload = swiftObject.getPayload();
            InputStream in = payload.openStream();
            FileOutputStream out = new FileOutputStream(tempFile);
            IOUtils.copy(in, out);
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(payload);
            boolean renameTo = tempFile.renameTo(finalFile);
            long end = System.currentTimeMillis() - begin;
            LOG.info("Download Object {} from {} success. fileSize: {} bytes , cost {} ms. rename: {} ", objectName, containerName, finalFile.length(), end, renameTo);
        } catch (IOException e) {
            LOG.error("Download Object {}:{} failed!",containerName, objectName);
            throw new RuntimeException("Download Object " + objectName + " failed!", e);
        }
    }

}
